package com.osilon.util;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import com.osilon.Osilon;
import com.osilon.hooks.ChatCommand;
import com.osilon.hooks.MinecraftServer;
import com.osilon.plugins.Plugin;

public class PluginCache {
	private File dir;
	private List<Plugin> plugins = new ArrayList<Plugin>();
	private ClassLoader loader;
	
	private FileFilter filter = new FileFilter() {
		@Override
		public boolean accept(File pathname) {
			return !pathname.isDirectory() && pathname.getName().endsWith(".jar");
		}
	};
	
	public PluginCache(File dir) {
		this.dir = dir;
	}
	
	public void loadPlugins() {
		List<JarFile> plugins = new ArrayList<JarFile>();
		
		for(File f : dir.listFiles(filter)) {
			try {
				JarFile jar = new JarFile(f);
				Manifest m = jar.getManifest();
				
				if(m == null) continue;
				
				Attributes a = m.getMainAttributes();
				
				if(a == null) continue;
		
				plugins.add(jar);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		initClassLoader(plugins);
		initPlugins(plugins);
	}
	
	private void initClassLoader(List<JarFile> plugins) {
		List<URL> urls = new ArrayList<URL>();
		
		for(JarFile f : plugins) {
			try {
				urls.add(new URL("jar:" + new File(f.getName()).toURI().toURL() + "!/"));
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
		}
		loader = new URLClassLoader(urls.toArray(new URL[0]));
	}
	
	private void initPlugins(List<JarFile> pluginJars) {
		System.out.println("Loading Plugins");
		for(JarFile jar : pluginJars) {
			try {
				Manifest m = jar.getManifest();
				Attributes a = m.getMainAttributes();
				String mainClass = a.getValue(Attributes.Name.MAIN_CLASS);
				
				if(mainClass == null) {
					System.out.println("  * " + jar.getName() + ": No main-class attribute in manifest.");
					continue;
				}
				
				try {
					Class<?> c = loader.loadClass(mainClass);
					
					if(!Plugin.class.isAssignableFrom(c)) {
						System.out.println("  * " + jar.getName() + ": " + c.getSimpleName() + " doesn't implement 'Plugin' interface.");
						continue;
					}
					
					Constructor<?> cons = c.getConstructor(MinecraftServer.class);
					
					if(cons == null) {
						System.out.println("  * " + jar.getName() + ": " + c.getSimpleName() + " expected to have constructor: " + c.getSimpleName() + "(MinecraftServer server)");
						continue;
					}
					
					Plugin plugin = (Plugin) cons.newInstance(Osilon.getMinecraftServer());
					plugins.add(plugin);
					
					System.out.println("  - Loaded Plugin: " + plugin.getName());
				} catch (Exception e) {
					e.printStackTrace();
				}
			} catch(IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void process(ChatCommand cc, String command, String args) {
		for(Plugin p : plugins) {
			boolean handles = false;
			
			for(String s : p.getSupportedCommands()) {
				if(s.equalsIgnoreCase(command)) {
					handles = true;
					break;
				}
			}
			
			if(handles) {
				p.process(Osilon.getMinecraftServer(), cc);
			}
		}
	}
}
